package forumreaction

import (
	"context"
	"fmt"
	"strconv"

	"mall/service/forum/api/internal/svc"

	"github.com/sirupsen/logrus"
)

type (
	// ForumReaction is an interface to be customized, add more methods here,
	// and implement the added methods in customForumReaction.
	ForumReaction interface {
		Do(objectId string, userId string) (int64, error)                                         // 点赞
		Undo(objectId string, userId string) (int64, error)                                       // 取消点赞
		Done(objectId string, userId string) (bool, error)                                        // 是否点赞
		Count(objectId string) (int64, error)                                                     // 点赞总数
		GetReactionMap(objectIds []string, userId string) (map[int64]*ForumReactionStatus, error) // 批量获取点赞状态及总数
		InitStatus(objectId string, userId string, status string) (bool, error)                   // 初始化
		UpdateStatus(objectId string, userId string, status string) (bool, error)                 // 同步
		InitCount(objectId string, count string) (bool, error)                                    // 初始化
		UpdateCount(objectId string, count string) (bool, error)                                  // 同步
	}

	customForumReaction struct {
		ctx      context.Context
		svcCtx   *svc.ServiceContext
		redisKey string
	}
)

func NewForumReaction(ctx context.Context, svcCtx *svc.ServiceContext, redisKey string) ForumReaction {
	return &customForumReaction{
		ctx:      ctx,
		svcCtx:   svcCtx,
		redisKey: redisKey,
	}
}

// 每个对象的点赞关系
func (l *customForumReaction) getReactionKey(objectId string, userId string) string {
	return fmt.Sprintf("{%s:%s}:%s", l.redisKey, objectId, userId)
}

// 每个对象的点赞数
func (l *customForumReaction) getCountKey(objectId string) string {
	return fmt.Sprintf("{%s:%s}", l.redisKey, objectId)
}

func (l *customForumReaction) Do(objectId string, userId string) (int64, error) {
	val, err := l.svcCtx.BizRedis.EvalCtx(l.ctx, luaDoScript, []string{
		l.getReactionKey(objectId, userId),
		l.getCountKey(objectId),
	})
	if err != nil {
		logrus.Info(fmt.Sprintf("ForumReaction Do err : %+v ", err))
		return -1, err
	}

	// logrus.Info(fmt.Sprintf("ForumReaction Do val : %+v ", val))

	if s, ok := val.(string); ok {
		return strconv.ParseInt(s, 10, 64)
	}

	return -1, nil
}

func (l *customForumReaction) Undo(objectId string, userId string) (int64, error) {
	val, err := l.svcCtx.BizRedis.EvalCtx(l.ctx, luaUndoScript, []string{
		l.getReactionKey(objectId, userId),
		l.getCountKey(objectId),
	})
	if err != nil {
		logrus.Info(fmt.Sprintf("ForumReaction Undo err : %+v ", err))
		return -1, err
	}

	// logrus.Info(fmt.Sprintf("ForumReaction Undo val : %+v ", val))

	if s, ok := val.(string); ok {
		return strconv.ParseInt(s, 10, 64)
	}

	return -1, nil
}

func (l *customForumReaction) Done(objectId string, userId string) (bool, error) {
	val, err := l.svcCtx.BizRedis.GetCtx(l.ctx, l.getReactionKey(objectId, userId))
	if err != nil {
		logrus.Info(fmt.Sprintf("ForumReaction Done err : %+v ", err))
		return false, err
	}

	if val == "1" {
		return true, nil
	}

	return false, nil
}

func (l *customForumReaction) Count(objectId string) (int64, error) {
	val, err := l.svcCtx.BizRedis.GetCtx(l.ctx, l.getCountKey(objectId))
	if err != nil {
		logrus.Info(fmt.Sprintf("ForumReaction Count err : %+v ", err))
		return 0, err
	}

	return strconv.ParseInt(val, 10, 64)
}

func (l *customForumReaction) InitStatus(objectId string, userId string, status string) (bool, error) {
	res, err := l.svcCtx.BizRedis.EvalCtx(l.ctx, luaInitScript, []string{
		l.getReactionKey(objectId, userId),
	}, status)
	if err != nil {
		logrus.Info(fmt.Sprintf("ForumReaction InitStatus err : %+v ", err))
		return false, err
	}

	if val, ok := res.(int64); ok && val > 0 {
		return true, nil
	}

	return false, nil
}

func (l *customForumReaction) UpdateStatus(objectId string, userId string, status string) (bool, error) {
	res, err := l.svcCtx.BizRedis.EvalCtx(l.ctx, luaUpdateScript, []string{
		l.getReactionKey(objectId, userId),
	}, status)
	if err != nil {
		logrus.Info(fmt.Sprintf("ForumReaction UpdateStatus err : %+v ", err))
		return false, err
	}

	if val, ok := res.(int64); ok && val > 0 {
		return true, nil
	}

	return false, nil
}

func (l *customForumReaction) InitCount(objectId string, count string) (bool, error) {
	res, err := l.svcCtx.BizRedis.EvalCtx(l.ctx, luaInitScript, []string{
		l.getCountKey(objectId),
	}, count)
	if err != nil {
		logrus.Info(fmt.Sprintf("ForumReaction InitCount err : %+v ", err))
		return false, err
	}

	if val, ok := res.(int64); ok && val > 0 {
		return true, nil
	}

	return false, nil
}

func (l *customForumReaction) UpdateCount(objectId string, count string) (bool, error) {
	res, err := l.svcCtx.BizRedis.EvalCtx(l.ctx, luaUpdateScript, []string{
		l.getCountKey(objectId),
	}, count)
	if err != nil {
		logrus.Info(fmt.Sprintf("ForumReaction UpdateCount err : %+v ", err))
		return false, err
	}

	if val, ok := res.(int64); ok && val > 0 {
		return true, nil
	}

	return false, nil
}

func (l *customForumReaction) GetReactionMap(
	objectIds []string,
	userId string,
) (map[int64]*ForumReactionStatus, error) {
	if len(objectIds) == 0 {
		return nil, nil
	}

	reactionMap := make(map[int64]*ForumReactionStatus)
	var withStatus bool // 混合查询：不仅仅是获取数量，还要获取当前用户的操作状态
	objIdMap := make(map[int]string)
	var keys []string
	if len(userId) > 0 {
		withStatus = true
		for idx, objectId := range objectIds {
			objIdMap[idx+1] = objectId
			keys = append(keys, l.getCountKey(objectId))
			keys = append(keys, l.getReactionKey(objectId, userId))
		}
	} else {
		for idx, objectId := range objectIds {
			objIdMap[idx+1] = objectId
			keys = append(keys, l.getCountKey(objectId))
		}
	}

	vals, err := l.svcCtx.BizRedis.MgetCtx(l.ctx, keys...)
	if err != nil {
		logrus.Info(fmt.Sprintf("ForumReaction GetReactionMap err : %+v ", err))
		return nil, err
	}

	var count int64
	var status int32
	for idx, val := range vals {
		if withStatus && idx%2 == 1 { // 混合查询时，偶数项为 status
			if val == "1" {
				status = 1
			} else if len(val) == 0 { // redis 无记录
				status = -1
			} else { // "-1" or "0" 数据库无记录(经查询证实)或已取消
				status = 0
			}
		} else { // 其他情形，解析 count
			if len(val) == 0 { // redis 无记录(nil => "")
				count = -1
			} else {
				if i, err := strconv.ParseInt(val, 10, 64); err == nil && i >= 0 {
					count = i
				} else {
					count = 0
				}
			}
		}

		if withStatus {
			if idx%2 == 1 {
				if objectId, ok := objIdMap[(idx/2)+1]; ok {
					if i, err := strconv.ParseInt(objectId, 10, 64); err == nil {
						reactionMap[i] = &ForumReactionStatus{
							Status: status,
							Count:  count,
						}
					}
				}
			}
		} else {
			if objectId, ok := objIdMap[idx+1]; ok {
				if i, err := strconv.ParseInt(objectId, 10, 64); err == nil {
					reactionMap[i] = &ForumReactionStatus{
						Count: count,
					}
				}
			}
		}
	}

	return reactionMap, nil
}
